// OpenGLControl.cpp : implementation file
//

#include "stdafx.h"
#include "MemSpyy.h"
#include "OpenGLControl.h"
#include ".\openglcontrol.h"
#include "psapi.h"

// uggg i don't like this
#include "MemSpyyDlg.h"

#define ZOOM_FACTOR   800

// COpenGLControl

IMPLEMENT_DYNAMIC(COpenGLControl, CWnd)
COpenGLControl::COpenGLControl()
{
	dc = NULL;
	m_CurrentStartX = 0;
	m_CurrentStartY = 0;
	m_PID = 0;
	m_OpenGLInitialized = false;
	m_Red = 1.0;
	m_Blue = 1.0;

	m_OriginY = 0;
	m_OriginX = 0;

	m_CurrentlySelected = -1;

	RegisterWindowClass();
}

COpenGLControl::~COpenGLControl()
{
	if (dc) //Only delete dc when really allocated
	{
		delete dc;
	}
}


BEGIN_MESSAGE_MAP(COpenGLControl, CWnd)
	ON_WM_SIZE()
	ON_WM_PAINT()
	ON_WM_ERASEBKGND()
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	ON_WM_RBUTTONDOWN()
	ON_WM_RBUTTONDBLCLK()
END_MESSAGE_MAP()

void COpenGLControl::InitGL()
{
	glShadeModel(GL_SMOOTH);
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	glClearDepth(1.0f);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

	m_OpenGLInitialized = true;
}

void COpenGLControl::DrawMemory( SIZE_T blockSizeBytes )
{
	GLfloat sizeK = (GLfloat) (blockSizeBytes / 1024);
	GLfloat xcoord;
	GLfloat ycoord;

	if ( sizeK <= ( ZOOM_FACTOR - m_CurrentStartX ) )
	{
		xcoord = m_CurrentStartX / ZOOM_FACTOR;
		ycoord = m_CurrentStartY / ZOOM_FACTOR;
		glVertex3f( xcoord, ycoord, 0 );

		m_CurrentStartX += sizeK;
		xcoord = m_CurrentStartX / ZOOM_FACTOR;
		ycoord = m_CurrentStartY / ZOOM_FACTOR;
		glVertex3f( xcoord, ycoord, 0 );
	}
	else
	{
		int numLines = (int) sizeK / (ZOOM_FACTOR * 2);
		if ( 1 < numLines )
		{
			for ( int x = 0; x < numLines; x++ )
			{
				xcoord = m_CurrentStartX / ZOOM_FACTOR;
				ycoord = m_CurrentStartY / ZOOM_FACTOR;
				glVertex3f( xcoord, ycoord, 0 );

				xcoord = 1.0;
				ycoord = m_CurrentStartY / ZOOM_FACTOR;
				glVertex3f( xcoord, ycoord, 0 );

				m_CurrentStartY -= 1;
				xcoord = -1.0;
				ycoord = m_CurrentStartY / ZOOM_FACTOR;
				glVertex3f( xcoord, ycoord, 0 );

				xcoord = m_CurrentStartX / ZOOM_FACTOR;
				ycoord = m_CurrentStartY / ZOOM_FACTOR;
				glVertex3f( xcoord, ycoord, 0 );

				sizeK -= (ZOOM_FACTOR * 2);
			}
		}

		GLfloat nextLine = sizeK - (ZOOM_FACTOR - m_CurrentStartX);

		xcoord = m_CurrentStartX / ZOOM_FACTOR;
		ycoord = m_CurrentStartY / ZOOM_FACTOR;
		glVertex3f( xcoord, ycoord, 0 );

		xcoord = 1.0;
		ycoord = m_CurrentStartY / ZOOM_FACTOR;
		glVertex3f( xcoord, ycoord, 0 );

		m_CurrentStartY -= 1;

		xcoord = -1.0;
		ycoord = m_CurrentStartY / ZOOM_FACTOR;
		glVertex3f( xcoord, ycoord, 0 );

		m_CurrentStartX = -ZOOM_FACTOR + nextLine;

		xcoord = m_CurrentStartX / ZOOM_FACTOR;
		ycoord = m_CurrentStartY / ZOOM_FACTOR;
		glVertex3f( xcoord, ycoord, 0 );
	}
}

void COpenGLControl::DrawGLScene( bool inSelect )
{
	if ( m_PID == 0 )
		return;

	CheckInit();

	if ( inSelect )
		glInitNames();

	unsigned long largestFreeBlock = 0;
	unsigned long totalFree = 0;
	unsigned long totalDLL = 0;

	m_CurrentStartX = -ZOOM_FACTOR;
	m_CurrentStartY = ZOOM_FACTOR;


	glMatrixMode(GL_MODELVIEW);  
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Get a handle to the process.
	HANDLE process = OpenProcess( PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,false, m_PID );

	PVOID baseAddress = 0;
	SIZE_T retVal = 1;
	unsigned long numBlocks = 0;
	while (retVal)
	{
		MEMORY_BASIC_INFORMATION memBlock;
		retVal = VirtualQueryEx(process, baseAddress,&memBlock,sizeof(memBlock));
		
		if ( inSelect )
		{
			glPushName( numBlocks );
			m_MemoryBlocks[numBlocks] = baseAddress;
		}

		glBegin(GL_LINES);
		if (retVal)
		{

			if (memBlock.State & MEM_FREE)
			{
				if (memBlock.RegionSize>largestFreeBlock)
					largestFreeBlock = (unsigned long) memBlock.RegionSize;

				totalFree += (unsigned long) memBlock.RegionSize;

				if ( numBlocks == m_CurrentlySelected )
					glColor3f( 1.0f, 1.0f, 1.0f );
				else
					glColor3f(m_Red,0.0f,0.0f);
				DrawMemory( memBlock.RegionSize );
			}
			else
			{
				char mappedFile[255];
				if ( GetMappedFileName( process, baseAddress, mappedFile, 255) )
				{
					totalDLL += (unsigned long) memBlock.RegionSize;

					if ( numBlocks == m_CurrentlySelected )
						glColor3f( 1.0f, 1.0f, 1.0f );
					else
						glColor3f(0.0f,1.0f,0.0f);
				}
				else
				{
					if ( numBlocks == m_CurrentlySelected )
						glColor3f( 1.0f, 1.0f, 1.0f );
					else
						glColor3f(0.0f,0.0f,m_Blue);
				}


				DrawMemory( memBlock.RegionSize );
			}

			baseAddress = (void *) (((char *) baseAddress) + memBlock.RegionSize);
		}

		glEnd();

		if ( inSelect )
			glPopName();

		numBlocks++;
	}

	CMemSpyyDlg* theParent = (CMemSpyyDlg*) GetParent();
	theParent->SetLargestFree( largestFreeBlock );
	theParent->SetTotalDLL( totalDLL );
	theParent->SetTotalFree( totalFree );

	SwapBuffers(dc->m_hDC);

}



// COpenGLControl message handlers


void COpenGLControl::OnSize(UINT nType, int cx, int cy)
{
	CWnd::OnSize(nType, cx, cy);


	// this isn't used (yet) since the dialog doesn't resize
	if (cy == 0)
	{
		cy = 1;
	}

	m_Width = cx;
	m_Height = cy;


	glViewport(0,0,cx,cy);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	glOrtho(-1.0f,1.0f,-1.0f,1.0f,1.0f,-1.0f);

}



void COpenGLControl::OnPaint()
{

	CheckInit();

	ForceDraw();

	CWnd::OnPaint();
}
void COpenGLControl::ForceDraw()
{
	/** OpenGL section **/

	openGLDevice.makeCurrent();

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	DrawGLScene();
}

BOOL COpenGLControl::OnEraseBkgnd(CDC* pDC)
{
	return TRUE;
}

BOOL COpenGLControl::RegisterWindowClass()
{
	WNDCLASS wndcls;
	HINSTANCE hInst = AfxGetInstanceHandle();

	if (!(::GetClassInfo(hInst, _T("MFCOpenGLControl"), &wndcls)))
	{
		// otherwise we need to register a new class
		wndcls.style            = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
		wndcls.lpfnWndProc      = ::DefWindowProc;
		wndcls.cbClsExtra       = wndcls.cbWndExtra = 0;
		wndcls.hInstance        = hInst;
		wndcls.hIcon            = NULL;
		wndcls.hCursor          = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
		wndcls.hbrBackground    = (HBRUSH) (COLOR_3DFACE + 1);
		wndcls.lpszMenuName     = NULL;
		wndcls.lpszClassName    = _T("MFCOpenGLControl");

		if (!AfxRegisterClass(&wndcls))
		{
			AfxThrowResourceException();
			return FALSE;
		}
	}

	return TRUE;
}

void COpenGLControl::OnLButtonDown(UINT nFlags, CPoint point)
{
	m_PrevLDownY = point.y;
	m_PrevLDownX = point.x;

	CWnd::OnLButtonDown(nFlags, point);
}

void COpenGLControl::OnMouseMove(UINT nFlags, CPoint point)
{
	if ( nFlags == MK_RBUTTON)
	{
		if ( m_PrevRDownY < point.y )
			m_Height -=  (3 * (point.y - m_PrevRDownY));
		else
			m_Height +=  (3 * (m_PrevRDownY - point.y));

		m_PrevRDownY = point.y;

		if ( m_PrevRDownX < point.x )
			m_Width +=  (3 * (point.x - m_PrevRDownX));
		else
			m_Width-=  (3 * (m_PrevRDownX - point.x));

		m_PrevRDownX = point.x;
	}
	else if ( nFlags == MK_LBUTTON)
	{
		if ( m_PrevLDownY < point.y )
			m_OriginY -=  point.y - m_PrevLDownY;
		else
			m_OriginY +=  m_PrevLDownY - point.y;

		m_PrevLDownY = point.y;

		if ( m_PrevLDownX < point.x )
			m_OriginX +=  point.x - m_PrevLDownX;
		else
			m_OriginX -=  m_PrevLDownX - point.x;

		m_PrevLDownX = point.x;

	}
	else
	{
		m_PrevX = point.x;
		m_PrevY = point.y;

		CMemSpyyDlg* theParent = (CMemSpyyDlg*) GetParent();


		//////// STATUS /////////////
		GLuint selectBuffer[100000];
		glSelectBuffer(100000, selectBuffer);

		glRenderMode(GL_SELECT);

		CRect rect;
		GetWindowRect(&rect);

		long halfWidth = rect.Width() / 2;
		long halfHeight = rect.Height() / 2;

		GLdouble xcoord = 0;
		if ( point.x < halfWidth )
			xcoord = -( (double) point.x / (double) halfWidth );
		else
			xcoord = ((double) (point.x - halfWidth)) / (double) halfWidth;

		GLdouble ycoord = 0;
		if ( point.y < halfHeight )
			ycoord = ((double) halfHeight - (double) point.y) / ((double) halfHeight);
		else
			ycoord = - ( (double) ( point.y - halfHeight ) / (double) halfHeight );

		// used for debugging
//		theParent->UpdateXY( xcoord, ycoord );

		
		GLint viewport[4];
		glGetIntegerv( GL_VIEWPORT, viewport );

		glMatrixMode(GL_PROJECTION);  
		glLoadIdentity();
		gluPickMatrix( point.x, rect.Height() - point.y - 1, 2, 2, viewport );

		DrawGLScene(true);
		glFlush();

		long hits = glRenderMode( GL_RENDER );

		if (hits > 0 )
		{
			int glItem = selectBuffer[3];

			theParent->UpdateStatus( m_MemoryBlocks[glItem] );
			m_CurrentlySelected = glItem;
		}
	}

	glViewport(m_OriginX,m_OriginY,m_Width,m_Height);
	ForceDraw();

	CWnd::OnMouseMove(nFlags, point);
}

void COpenGLControl::OnRButtonDown(UINT nFlags, CPoint point)
{
	m_PrevRDownY = point.y;
	m_PrevRDownX = point.x;


	CWnd::OnRButtonDown(nFlags, point);
}

void COpenGLControl::CheckInit()
{
	if ( !m_OpenGLInitialized )
	{
		CRect rect;
		GetWindowRect(&rect);

		m_Width = m_DefaultWidth = rect.Width();
		m_Height = m_DefaultHeight = rect.Height();

		dc = new CClientDC(this); //Get device context of window
		openGLDevice.create(dc->m_hDC); //Create opengl rendering context
		InitGL();
	}
}
void COpenGLControl::OnRButtonDblClk(UINT nFlags, CPoint point)
{
	m_OriginX = 0;
	m_OriginY = 0;
	m_Width = m_DefaultWidth;
	m_Height = m_DefaultHeight;

	glViewport(m_OriginX,m_OriginY,m_Width,m_Height);
	ForceDraw();

	CWnd::OnRButtonDblClk(nFlags, point);
}
